<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: #eee;
      }
      .table-box {
        width: 800px;
        max-width: 90vw;
        position: relative;
      }
      table {
        width: 100%;
        background-color: #fff;
        border-spacing: 0;
        border-collapse: collapse;
        table-layout: fixed;
      }
      th {
        background-color: #e7f3ff;
        height: 25px;
        cursor: pointer;
      }
      th,
      td {
        color: #333;
        font-size: 15px;
        text-align: center;
        padding: 8px 10px;
        border: 1px solid #ddd;
      }
      .box {
        background-color: rgba(0, 0, 0, 0.8);
        color: #fff;
        height: 100%;
        text-align: center;
        position: absolute;
        top: 0;
        left: 0;
        cursor: move;
        display: none;
      }
      .box p {
        line-height: 37.5px;
        font-size: 15px;
      }
    </style>
  </head>
  <body>
    <div class="table-box">
      <table>
        <tbody>
          <tr>
            <th title="点击排序" onclick="sortTable(0)">座号</th>
            <th title="点击排序" onclick="sortTable(1)">姓名</th>
            <th title="点击排序" onclick="sortTable(2)">性别</th>
            <th title="点击排序" onclick="sortTable(3)">年龄</th>
            <th title="点击排序" onclick="sortTable(4)">分数</th>
          </tr>
          <tr>
            <td>1</td>
            <td>张珊</td>
            <td>女</td>
            <td>18</td>
            <td>80</td>
          </tr>
          <tr>
            <td>2</td>
            <td>组件</td>
            <td>男</td>
            <td>18</td>
            <td>90</td>
          </tr>
          <tr>
            <td>3</td>
            <td>记得你</td>
            <td>女</td>
            <td>18</td>
            <td>22</td>
          </tr>
          <tr>
            <td>4</td>
            <td>张珊</td>
            <td>女</td>
            <td>18</td>
            <td>80</td>
          </tr>
          <tr>
            <td>5</td>
            <td>黎明</td>
            <td>男</td>
            <td>18</td>
            <td>80</td>
          </tr>
          <tr>
            <td>6</td>
            <td>直白</td>
            <td>女</td>
            <td>18</td>
            <td>18</td>
          </tr>
          <tr>
            <td>7</td>
            <td>找你</td>
            <td>男</td>
            <td>88</td>
            <td>65</td>
          </tr>
        </tbody>
      </table>
      <div class="box"></div>
    </div>
    <script>
      function sortTable(idx) {
        let otable = document.querySelector("table"), //获取表格
          obody = otable.tBodies[0], //表格正文tbody
          otr = obody.rows, //表格行
          tarr = []; //临时数组
        for (let i = 1; i < otr.length; i++) {
          // 除表头都赋值给数组
          tarr[i - 1] = otr[i];
        }
        // sortCol,自定义属性，用于判断排序
        if (obody.sortCol === idx) {
          tarr.reverse();
        } else {
          tarr.sort((tr1, tr2) => {
            let value1 = tr1.cells[idx].innerHTML;
            let value2 = tr2.cells[idx].innerHTML;
            if (!isNaN(value1) && isNaN(value2)) {
              // 数字排序
              return value1 - value2;
            } else {
              // 字符串排序
              return value1.localeCompare(value2);
            }
          });
        }
        // 创建虚拟节点对象，节点对象包含所有的属性和方法
        let fragment = document.createDocumentFragment();
        for (let i = 1; i < tarr.length; i++) {
          // 排序好的数组添加进去
          fragment.appendChild(tarr[i]);
        }
        // 把节点对象追加进去
        obody.appendChild(fragment);
        // 更新sortCol
        obody.sortCol = idx;
      }

      // 拖拽排序
      function DragSort() {
        let otbox = document.querySelector(".table-box"), //
          otable = document.querySelector("table"), //获取表格
          obody = otable.tBodies[0], //表格正文tbody
          oth = obody.getElementsByTagName("th"),
          otd = obody.getElementsByTagName("td"),
          box = document.querySelector(".box"), //拖拽时的悬浮层
          arrn = []; //记录每一个th向左偏移量

        for (let i = 0; i < otd.length; i++) {
          otd[i].onmousedown = function (e) {
            var e = e || window.event, //事件对象
              target = e.target, //目标
              thw = target.offsetWidth, //目标的偏移宽度
              maxL = otbox.offsetWidth - thw, //计算最大向左偏移量
              rows = otable.rows, //表格的行
              tboxL = otbox.offsetLeft, //表格容器向左偏移量
              disX = target.offsetLeft, //目标向左偏移量
              that = this, //把当前单元格对象（鼠标按下的位置）记录
              cdisX = e.clientX - tboxL - disX; //计算鼠标光标在悬浮层中向左偏移量
            // 把当前单元格的同一列数据填充到悬浮层
            for (let i = 0; i < rows.length; i++) {
              let op = document.createElement("p");
              op.innerHTML = rows[i].cells[this.cellIndex].innerHTML;
              box.appendChild(op);
            }
            // 记录每一个th的向左偏移量
            for (let i = 0; i < oth.length; i++) {
              arrn.push(oth[i].offsetLeft);
            }
            // 显示悬浮层
            box.style.display = "block";
            box.style.width = thw + "px";
            box.style.left = disX + "px";
            // 鼠标移动时间
            document.onmouseover = (e) => {
              // 根据鼠标移动，实时设置悬浮层位置
              box.style.top = 0;
              box.style.left = e.clientX - tboxL - cdisX + "px";
              if (box.offsetLeft > maxL) {
                box.style.left = maxL + "px";
              } else if (box.offsetLeft < 0) {
                box.style.left = 0;
              }
              document.onselectstart = function () {
                return false;
              };
            };
            // 鼠标松开
            document.onmouseup = (e) => {
              let ops = box.getElementsByTagName("p"),
                oboxL = box.offsetLeft + cdisX;
              for (let i = 0; i < arrn.length; i++) {
                if (arrn[i] < oboxL) {
                  var index = i;
                }
              }
              for (let i = 0; i < rows.length; i++) {
                // 交换数据
                rows[i].cells[that.cellIndex].innerHTML = "";
                rows[i].cells[that.cellIndex].innerHTML =
                  rows[i].cells[index].innerHTML;
                rows[i].cells[index].innerHTML = "";
                rows[i].cells[index].innerHTML = ops[i].innerHTML;
              }
              // 重置
              box.innerHTML = "";
              arrn = [];
              box.style.display = "none";
              document.onmousemove = null;
              document.onmouseup = null;
              document.onselectstart = () => false;
            };
          };
        }
      }
      DragSort();
    </script>
  </body>
</html>
